#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls.Primitives;
using System.Windows;
using UTable.Objects.Controls;
using System.Collections.ObjectModel;

namespace UTable.Objects
{
	public class ShareCenter
	{
		public ShareCenter()
		{
			ShareUnits = new Collection<ShareUnit>();
			UnitsMap = new Dictionary<string, ShareUnit>();
		}

		protected Collection<ShareUnit> ShareUnits { get; set; }

		protected Dictionary<String, ShareUnit> UnitsMap { get; set; }

		protected ShareUnit FindShareUnit(UObject shareItem)
		{
			return FindShareUnit(shareItem.ID);
		}

		protected ShareUnit FindShareUnit(String id)
		{
			lock (UnitsMap)
			{
				if (UnitsMap.ContainsKey(id))
					return UnitsMap[id];
				else
					return null;
			}
		}

		protected UObject FindUObject(IObject target)
		{
			UObject reference = null;
			if (target is UObject)
				reference = target as UObject;
			if (target is UObjectProxy)
				reference = (target as UObjectProxy).Content;
			return reference;
		}

		protected ShareUnit CreateShareUnit(UObject reference, bool isMaster)
		{
			ShareUnit unit = new ShareUnit();
			lock (ShareUnits)
			{
				ShareUnits.Add(unit);
			}
			AddShareItem(unit, reference, isMaster);
			return unit;
		}

		protected void AddShareItem(ShareUnit unit, UObject reference, bool isMaster)
		{
			ShareItem item = new ShareItem();
			item.ID = reference.ID;
			item.IsMaster = isMaster;
			item.Object = reference;
			item.Dispatcher = reference.Dispatcher;
			
			lock (UnitsMap)
			{
				unit.Items.Add(item);
				UnitsMap.Add(item.ID, unit);
			}
		}

		public IObject CreateCopy(UObject reference, ObjectPlacementMode placement, Vector offset)
		{
			// create the object
			ObjectCreateParameter param = new ObjectCreateParameter(reference.ObjectType);
			IObject target = UTableHelper.CreateObject(param);

			// set the position of this object
			Point refPosition = reference.Position;
			Size refSize = new Size(reference.Width, reference.Height);
			if (!reference.OwnEntity)
			{
				// it's a control within a object
				refPosition = reference.TranslatePoint(new Point(0, 0), UTableHelper.CurrentVisualTree as UIElement);
				refSize = new Size(reference.ActualWidth, reference.ActualHeight);
			}

			Point position = new Point();
			switch (placement)
			{
				case ObjectPlacementMode.Bottom:
					position = new Point(offset.X + refPosition.X, refSize.Height + offset.Y + refPosition.Y);
					break;
				case ObjectPlacementMode.Left:
					position = new Point(offset.X + refPosition.X - refSize.Width, offset.Y + refPosition.Y);
					break;
				case ObjectPlacementMode.Right:
					position = new Point(offset.X + +refPosition.X + refSize.Width, offset.Y + refPosition.Y);
					break;
				case ObjectPlacementMode.Top:
					position = new Point(offset.X + refPosition.X, offset.Y - refSize.Height + refPosition.Y);
					break;
				case ObjectPlacementMode.Center:
					position = new Point(offset.X + refPosition.X, offset.Y + refPosition.Y);
					break;
				case ObjectPlacementMode.Relative:
					position = new Point(offset.X + refPosition.X, offset.Y + refPosition.Y);
					break;
			}
			target.Position = position;
			target.Width = refSize.Width;
			target.Height = refSize.Height;
			target.ScaleX = reference.ScaleX;
			target.ScaleY = reference.ScaleY;
			target.Orientation = reference.Orientation;

			// set the owner of this object
			if (reference.OwnEntity)
			{
				target.Owner = reference.Owner;
			}
			else
			{
				target.Owner = UTableHelper.CurrentVisualTree as IObject;
			}

			// set the model of this object
			object model = reference.GetModel();
			target.SendMessage(new InitializeModelMessage(model));
			return target;
		}

		public IObject CreateShare(UObject reference, bool isMaster, ObjectPlacementMode placement, Vector offset)
		{
			IObject target = CreateCopy(reference, placement, offset);
			// check whether this object is in a share unit
			ShareUnit unit = FindShareUnit(reference);
			if (unit == null)
			{
				// create the share unit
				unit = CreateShareUnit(reference, isMaster);
			}
			AddShareItem(unit, (target as UObjectProxy).Content, isMaster);
			return target;
		}

		public void AddShare(IObject source, IObject target, bool isMaster)
		{
			ShareUnit unit = FindShareUnit(source.ID);
			UObject reference = FindUObject(target);
            bool addSource = false;
			if (unit == null)
			{
				unit = CreateShareUnit(reference, isMaster);
                addSource = true;
			}
			else
			{
				AddShareItem(unit, reference, isMaster);
			}

			// set the model of this object
			object model = source.QueryData(new QueryModelMessage());
			reference.Dispatcher.BeginInvoke(new SendMessageHandler(reference.SendMessage), new object[] { new InitializeModelMessage(model) });

            if (addSource)
            {
                AddShareItem(unit, FindUObject(source), isMaster);
            }
		}

		public void RemoveShare(UObject shareItem)
		{
			ShareUnit unit = FindShareUnit(shareItem);
			if (unit != null)
			{
				lock (UnitsMap)
				{
					UnitsMap.Remove(shareItem.ID);
					ShareItem item = null;
					foreach (ShareItem i in unit.Items)
						if (i.ID == shareItem.ID)
							item = i;
					unit.Items.Remove(item);
				}
				lock (ShareUnits)
				{
					if (unit.Items.Count == 0)
					{
						ShareUnits.Remove(unit);
					}
				}
			}
		}

		public void UpdateModel(UObject source, object message)
		{
			ShareUnit unit = FindShareUnit(source);
			if (unit != null)
			{
				lock (UnitsMap)
				{
					foreach (ShareItem item in unit.Items)
					{
						if (item.ID != source.ID)
						{
							item.Dispatcher.BeginInvoke(new SendMessageHandler(item.Object.SendMessage), new object[] { message });
						}
					}
				}
			}
		}

		public bool IsSharing(IObject reference)
		{
			ShareUnit unit = FindShareUnit(FindUObject(reference));
			return unit != null;
		}

	}

	public enum ObjectPlacementMode
	{
		Relative,
		Bottom,
		Center,
		Right,
		Left,
		Top,
	}

	public class InitializeModelMessage
	{
		public InitializeModelMessage(object model)
		{
			Model = model;
		}
		public object Model { get; protected set; }
	}

	public class UpdateModelMessage
	{
		public UpdateModelMessage(object msg)
		{
			UpdateMessage = msg;
		}
		public object UpdateMessage { get; protected set; }
	}

	public class QueryModelMessage
	{
		
	}
}
